import copy
import json
import os
import sys
import time

import openpyxl

from PyQt5.QtChart import QChartView, QChart, QLineSeries
from PyQt5.QtCore import QMargins, Qt, QModelIndex, QThread, pyqtSignal
from PyQt5.QtGui import QPainter, QStandardItemModel, QStandardItem, QIcon, QPixmap
from PyQt5.QtWidgets import *

from common_fun import convert_to_type, save_json
from widgets.widgets import InstrumentInfoWidget

from frequencies import wifi_5g_frequencies, wifi_6g_frequencies, \
    frequencies_of_2_point_4, frequencies_of_class_bluetooth


LOSS_PATH = 'C:\\Temp\\Loss\\'

vsg_model_list = [
    'N5182B', 'N5172B', 'N5182A', 'N5181A', 'E4438C', 'KeysightVectorSignalGenerators',
    'SMW200A', 'SMU200A', 'SMBV100B', 'SMBV100A',
    'SMM100A', 'SGT100A', 'KS_XAPP','N5173B'
]

asg_model_list = [
'N5181B', 'N5182B', 'N5172B', 'N5183B', 'N5173B', 'E8257D', 'SMW200A', 'E4422B', 'SMB100A', 'N5171B', 'N5183B', 'KS_XAPP', 'SMB100B', 'SMA100B', 'KeysightAnalogSignalGenerators'
]

sa_model_list = ['Keysight', 'R&S']
switch_box_list = ['SMU3002', 'SMU3003', 'CSKJ3002']


class CalibrationThreadOfSwitchBox(QThread):
    waveform_changed_signal = pyqtSignal(str)
    line_loss_flash_signal = pyqtSignal(list)
    save_loss_info_signal = pyqtSignal(dict)
    # message
    message_signal = pyqtSignal(str)
    waveform_progress_signal = pyqtSignal(int)
    power_progress_signal = pyqtSignal(int)

    def __init__(self):
        super().__init__()
        # 测试任务
        self.tasks = []
        self.sa = None
        self.vsg = None
        self.power_sensor = None
        self.switch_box = None
        self.lowest = None
        self.highest = None
        self.powers = []

        self.current_waveform = None
        self.current_frequency = None

        self.is_blocking = False

        self.object_of_calibration = None

        self.loss_info = {}
        self.line_loss_info = {}
        self.is_load_data = False

        self.stop_working = False

    def calibration_line_loss(self):
        self.line_loss_info = {}
        # 设置仪器仪表
        reference_level = -10
        # 其他
        self.sa.xapp_connectivity_set_sa_rflevel(reference_level + 15)
        self.vsg.set_amplitude(reference_level)
        self.sa.set_detector('RMS')
        self.sa.set_trace_mode('MAXHOLD')
        self.sa.xapp_nr_set_continue_state("OFF")
        self.vsg.set_rf(1)
        current_span = None
        current_rbw = None
        current_vbw = None
        for i, task in enumerate(self.tasks):
            if self.stop_working:
                break
            self.message_signal.emit("--"* 20)
            # 频率一致
            frequency = task['center frequency']
            self.sa.xapp_connectivity_set_center_frequency(f"{frequency} MHz")
            self.vsg.set_frequency(f"{frequency} MHz")
            # 其他
            if (current_span != task['span']) or (current_rbw != task['rbw']) or (current_vbw != task['vbw']):
                self.sa.xapp_connectivity_set_sa_freq_span(task['span'])
                self.sa.xapp_connectivity_set_sa_res_bandwidth(task['rbw'])
                self.sa.xapp_connectivity_set_sa_video_bandwidth(task['vbw'])
                current_span = task['span']
                current_rbw = task['rbw']
                current_vbw = task['vbw']
            self.sa.restart()
            # 测量
            if not (('Keysight' in self.sa.brand) or ('Agilent Technologies' in self.sa.brand) or ('Prosund Technologies' in self.sa.brand)):
                self.sa.init_obw()
                self.sa.xapp_common_query_oper_stat()
                time.sleep(0.2)
                self.sa.xapp_connectivity_get_sa_peak(1)
                time.sleep(0.2)
                peak_power = self.sa.get_mark_power(1)
            else:
                # self.sa.xapp_common_query_oper_stat()
                time.sleep(0.2)
                self.sa.xapp_connectivity_get_sa_peak(1)
                time.sleep(0.1)
                peak_power = self.sa.get_mark_power(1)
            # 计算Loss
            loss = reference_level - peak_power
            loss = round(loss, 2)
            if loss < 0:
                loss = 0
            if loss == 0:
                loss = 0
            frequency = round(float(frequency), 2)
            self.line_loss_info[str(frequency)] = loss
            xx = (i + 1) * (100 / len(self.tasks))
            progress = int(xx)
            self.power_progress_signal.emit(progress)
            self.message_signal.emit(f"*** Frequency: {frequency} MHz, Loss: {loss} dBm; ***")
        self.vsg.set_rf(0)
        self.save_loss_info_signal.emit(self.line_loss_info)

    def calibration_switch_box(self):
        if not self.is_load_data:
            self.loss_info = {}
        # 设置仪器仪表
        reference_level = -20
        # 其他
        self.sa.xapp_connectivity_set_sa_rflevel(reference_level + 15)
        self.vsg.set_amplitude(reference_level)
        self.sa.set_detector('RMS')
        self.sa.set_trace_mode('MAXHOLD')
        self.sa.xapp_nr_set_continue_state("OFF")
        self.vsg.set_rf(1)
        current_span = None
        current_rbw = None
        current_vbw = None
        for i, task in enumerate(self.tasks):
            if self.stop_working:
                break
            self.message_signal.emit("--" * 20)
            # 频率一致
            frequency = task['center frequency']
            self.sa.xapp_connectivity_set_center_frequency(f"{frequency} MHz")
            self.vsg.set_frequency(f"{frequency} MHz")
            # 其他
            if (current_span != task['span']) or (current_rbw != task['rbw']) or (current_vbw != task['vbw']):
                self.sa.xapp_connectivity_set_sa_freq_span(task['span'])
                self.sa.xapp_connectivity_set_sa_res_bandwidth(task['rbw'])
                self.sa.xapp_connectivity_set_sa_video_bandwidth(task['vbw'])
                current_span = task['span']
                current_rbw = task['rbw']
                current_vbw = task['vbw']
            # 测量
            self.sa.restart()
            if not (('Keysight' in self.sa.brand) or ('Agilent Technologies' in self.sa.brand) or ('Prosund Technologies' in self.sa.brand)):
                # self.sa.xapp_nr_set_continue_state('ON')
                # time.sleep(1)
                # self.sa.xapp_nr_set_continue_state('OFF')
                self.sa.xapp_common_query_oper_stat()
                time.sleep(0.5)
                self.sa.xapp_connectivity_get_sa_peak(1)
                time.sleep(0.2)
                peak_power = self.sa.get_mark_power(1)
            else:
                # self.sa.xapp_common_query_oper_stat()
                time.sleep(0.2)
                self.sa.xapp_connectivity_get_sa_peak(1)
                time.sleep(0.1)
                peak_power = self.sa.get_mark_power(1)
            # 获得a line loss
            try:
                frequencies_of_line_loss = list(self.line_loss_info.keys())
                closest_frequency = min(frequencies_of_line_loss, key=lambda x: abs(float(x) - float(frequency)))
                line_loss = self.line_loss_info[closest_frequency]
            except:
                line_loss = 0
                self.message_signal.emit("""<font color="#ed5a65"> get line loss fail!</font>""")
            # 计算Loss
            loss = reference_level - peak_power
            if loss < 0:
                loss = 0
            if loss == 0:
                loss = 0
            frequency = round(float(frequency), 2)
            self.loss_info[str(frequency)] = round(loss - line_loss, 2)
            xx = (i + 1) * (100 / len(self.tasks))
            progress = int(xx)
            self.power_progress_signal.emit(progress)
            loss = round(loss, 2)
            self.message_signal.emit(f"*** Frequency: {frequency} MHz, a line loss: {line_loss} dBm, Loss: {loss} dBm; ***")
        self.vsg.set_rf(0)
        self.save_loss_info_signal.emit(self.loss_info)

    def run(self) -> None:
        if self.object_of_calibration == 'Switch Box':
            self.calibration_switch_box()
        elif self.object_of_calibration == 'Line Loss':
            try:
                self.calibration_line_loss()
            except Exception as e:
                print(str(e))
                self.message_signal.emit(str(e))
        return None


class DifferenceMethodMainWindow(QDialog):
    def __init__(self, parent=None):
        super(DifferenceMethodMainWindow, self).__init__(parent)
        # 属性
        self.calibration_01_path = ''
        self.calibration_02_path = ''
        self.loss_file_path_list_01 = []
        self.loss_file_path_list_02 = []
        # 布局
        self.v = QVBoxLayout()
        self.calibration_01_button = QPushButton('Select the directly connected LOSS folder')
        self.calibration_02_button = QPushButton('Select the LOSS folder measured by the switch box')
        self.working_button = QPushButton('Both subtraction')
        self.v.addWidget(self.calibration_01_button)
        self.v.addWidget(self.calibration_02_button)
        self.v.addWidget(self.working_button)
        self.setLayout(self.v)

        # 信号与槽
        self.calibration_01_button.clicked.connect(self.select_calibration_01)
        self.calibration_02_button.clicked.connect(self.select_calibration_02)
        self.working_button.clicked.connect(self.subtract)

    def select_calibration_01(self):
        self.calibration_01_path, _f = QFileDialog.getOpenFileName(self, 'Select LOSS folder', 'C:\Temp\\Loss')
        print(self.calibration_01_path)

    def select_calibration_02(self):
        self.calibration_02_path, _f = QFileDialog.getOpenFileName(self, 'Select LOSS folder', 'C:\Temp\Loss')
        print(self.calibration_02_path)

    def subtract(self):
        new_loss_dict = {}
        # 打开线损文件并读取
        with open(self.calibration_01_path, 'r') as loss_file_01, open(self.calibration_02_path, 'r') as loss_file_02:
            loss_dict_1 = json.load(loss_file_01)
            loss_dict_2 = json.load(loss_file_02)
            keys = loss_dict_1.keys()
            for key in keys:
                value_1 = loss_dict_1[key]
                value_2 = loss_dict_2[key]
                new_loss_dict[key] = round(value_2 - value_1, 2)
        # 保存文件
        # 弹窗，询问要保存的到哪里。
        filename, file_format = QFileDialog.getSaveFileName(
            self,
            f'Save',
            'C:\\Temp\\Loss',
            'json(*.json)'
        )
        with open(filename, 'w', encoding='utf8') as f:
            json.dump(new_loss_dict, f, sort_keys=False, indent=4)
        QMessageBox.about(self, 'Success', 'Success!')


class SpinBoxDelegate(QItemDelegate):
    def __init__(self, parent=None):
        super(SpinBoxDelegate, self).__init__(parent)

    def paint(self, painter: QPainter, option: 'QStyleOptionViewItem', index: QModelIndex) -> None:
        value = index.model().data(index, Qt.DisplayRole)
        option.displayAlignment = Qt.AlignRight | Qt.AlignVCenter
        self.drawDisplay(painter, option, option.rect, str(convert_to_type(value)) if value is not None else None)
        self.drawFocus(painter, option, option.rect)

    def createEditor(self, parent: QWidget, option: 'QStyleOptionViewItem', index: QModelIndex) -> QWidget:
        spin_box = QDoubleSpinBox(parent)
        spin_box.setRange(-9999999, 9999999),
        spin_box.setDecimals(2)
        spin_box.editingFinished.connect(self.commit_and_close_editor)
        return spin_box

    def commit_and_close_editor(self):
        spin_box = self.sender()
        self.commitData.emit(spin_box)
        self.closeEditor.emit(spin_box)

    def setEditorData(self, editor: QWidget, index: QModelIndex) -> None:
        value = index.model().data(index, Qt.DisplayRole)
        value = float(value)
        editor.setValue(value if value is not None else 0)


class NewLossDialog(QDialog):
    def __init__(self, parent=None):
        super(NewLossDialog, self).__init__(parent)
        self.frequency_double_spin_box = QDoubleSpinBox()
        self.frequency_double_spin_box.setMaximum(30000)
        self.frequency_double_spin_box.setDecimals(2)
        self.loss_double_spin_box = QDoubleSpinBox()

        self.f_layout = QFormLayout()
        self.f_layout.addRow('Frequency (MHz)', self.frequency_double_spin_box)
        self.f_layout.addRow('Loss (dBm)', self.loss_double_spin_box)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.button(QDialogButtonBox.Ok).setDefault(True)
        self.f_layout.addWidget(self.button_box)

        self.setLayout(self.f_layout)

        # 信号与槽
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        self.frequency = None
        self.loss = None

    def accept(self):
        self.frequency = self.frequency_double_spin_box.value()
        self.loss = self.loss_double_spin_box.value()
        return QDialog.accept(self)


class UiLossDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.resize(1000, 700)
        icon = QIcon()
        icon.addPixmap(QPixmap(":/img/time_line"), QIcon.Normal, QIcon.Off)
        self.setWindowIcon(icon)
        self.setWindowTitle('Loss Setting')
        self.setWindowFlag(Qt.WindowMaximizeButtonHint)  # 添加最大化按钮
        ###############
        #   左边区域
        ###############
        # GroupBox: 路径
        self.group_box_of_path = QGroupBox("Path")
        self.group_box_of_path_layout = QVBoxLayout(self.group_box_of_path)
        # 内部线损或外部线损
        self.inside_or_outside_label = QLabel('Side:')
        self.inside_or_outside_combox = QComboBox()
        self.paths_label = QLabel('Paths')
        self.right_label = QLabel(' - ')
        self.entrance_combox = QComboBox()
        self.instrument_combox = QComboBox()
        self.path_layout = QHBoxLayout()
        self.path_layout.addWidget(self.entrance_combox)
        self.path_layout.addWidget(self.right_label)
        self.path_layout.addWidget(self.instrument_combox)

        self.group_box_of_path_layout.addWidget(self.inside_or_outside_label)
        self.group_box_of_path_layout.addWidget(self.inside_or_outside_combox)
        self.group_box_of_path_layout.addWidget(self.paths_label)
        self.group_box_of_path_layout.addLayout(self.path_layout)

        # GroupBox: Instrument(校准涉及到的仪器仪表)
        self.group_box_of_instrument = QGroupBox("Instrument")
        self.group_box_of_instrument_layout = QVBoxLayout(self.group_box_of_instrument)
        self.sg_info_widget = InstrumentInfoWidget('SG', asg_model_list + vsg_model_list)
        self.sa_info_widget = InstrumentInfoWidget('SA', sa_model_list)
        self.switch_box_widget = InstrumentInfoWidget('Switch Box', switch_box_list)
        self.group_box_of_instrument_layout.addWidget(self.sg_info_widget)
        self.group_box_of_instrument_layout.addWidget(self.sa_info_widget)
        self.group_box_of_instrument_layout.addWidget(self.switch_box_widget)

        # GroupBox: 校准的频率范围和特定的频点
        self.group_box_of_frequency_range = QGroupBox("Frequency Range")
        self.group_box_of_frequency_range_layout = QFormLayout(self.group_box_of_frequency_range)
        self.start_frequency_spin = QSpinBox()
        self.stop_frequency_spin = QSpinBox()
        self.step_frequency_spin = QDoubleSpinBox()
        self.step_frequency_spin.setMaximum(1000)
        self.rbw_editer_1 = QLineEdit()
        self.vbw_editer_1 = QLineEdit()
        self.span_editer_1 = QLineEdit()
        self.rbw_editer_2 = QLineEdit()
        self.vbw_editer_2 = QLineEdit()
        self.span_editer_2 = QLineEdit()
        self.group_box_of_frequency_range_layout.addRow("Start Frequency (MHz)", self.start_frequency_spin)
        self.group_box_of_frequency_range_layout.addRow("Stop Frequency (MHz)", self.stop_frequency_spin)
        self.group_box_of_frequency_range_layout.addRow("Step (MHz)", self.step_frequency_spin)
        self.group_box_of_frequency_range_layout.addRow("RBW(<1GHz)", self.rbw_editer_1)
        self.group_box_of_frequency_range_layout.addRow("VBW(<1GHz)", self.vbw_editer_1)
        self.group_box_of_frequency_range_layout.addRow("Span(<1GHz)", self.span_editer_1)
        self.group_box_of_frequency_range_layout.addRow("RBW(>1GHz)", self.rbw_editer_2)
        self.group_box_of_frequency_range_layout.addRow("VBW(>1GHz)", self.vbw_editer_2)
        self.group_box_of_frequency_range_layout.addRow("Span(>1GHz)", self.span_editer_2)
        # 默认数值
        self.stop_frequency_spin.setMaximum(40 * 1000)
        self.start_frequency_spin.setMaximum(40 * 1000)
        self.start_frequency_spin.setValue(30)
        self.stop_frequency_spin.setValue(26 * 1000)
        self.step_frequency_spin.setValue(10)
        self.rbw_editer_1.setText('100 kHz')
        self.vbw_editer_1.setText('300 kHz')
        self.rbw_editer_2.setText('1 MHz')
        self.vbw_editer_2.setText('3 MHz')
        self.span_editer_1.setText('10 MHz')
        self.span_editer_2.setText('100 MHz')

        # 指定/特殊频点
        self.specify_frequency_check_box = QCheckBox("Specify frequency(Blocking, Channels)")
        self.specify_frequency_check_box.setChecked(True)
        self.only_specify_frequency_check_box = QCheckBox("Only Specify frequency(Blocking, Channels)")

        self.left_layout = QVBoxLayout()
        self.left_layout.addWidget(self.group_box_of_path)
        self.left_layout.addWidget(self.group_box_of_instrument)
        self.left_layout.addWidget(self.group_box_of_frequency_range)
        self.left_layout.addWidget(self.specify_frequency_check_box)
        self.left_layout.addWidget(self.only_specify_frequency_check_box)

        # 弹簧
        vertical_spacer = QSpacerItem(20, 279, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.left_layout.addItem(vertical_spacer)
        # self.left_layout.addWidget(self.loss_table_view_label)
        # self.left_layout.addWidget(self.loss_table_view)
        ###############
        #   右边区域
        ###############
        # Tab
        self.tab_view = QTabWidget()
        # log View
        self.log_view = QPlainTextEdit()
        self.tab_view.addTab(self.log_view, 'Log')

        # 绘图相关
        self.chart_view = QChartView(self)
        self.chart_view.setContentsMargins(0, 0, 0, 0)
        self.chart_view.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self.line_chart = QChart()
        self.line_chart.setMargins(QMargins(0, 0, 0, 0))
        self.line_chart.layout().setContentsMargins(0, 3, 0, 0)
        self.line_chart.setContentsMargins(0, 0, 0, 0)
        self.chart_view.setChart(self.line_chart)
        self.tab_view.addTab(self.chart_view, 'Chart')

        # loss table
        self.loss_table_view = QTableView()
        self.tab_view.addTab(self.loss_table_view, 'Table')

        self.right_layout = QVBoxLayout()
        self.right_layout.addWidget(self.tab_view)

        # 进度条
        self.power_progress_bar = QProgressBar(self)
        self.power_progress_bar.setMaximum(100)
        self.power_progress_bar.setMinimum(0)
        self.right_layout.addWidget(self.power_progress_bar)

        # 按钮
        self.add_push_button = QPushButton('Add')
        self.remove_push_button = QPushButton('Remove row')
        self.remove_all_push_button = QPushButton('Remove all')
        self.save_push_button = QPushButton('Save')
        self.calibration_push_button = QPushButton('Calibration')
        self.calibration_a_line_push_button = QPushButton('Calibration a line')
        self.load_a_line_loss_push_button = QPushButton('Load a line loss')
        self.load_target_table_button = QPushButton("Load Calibration file")
        self.json_to_excell_button = QPushButton("json to excel")
        self.subtracting_push_button = QPushButton('Subtracting')
        self.subtracting_push_button.hide()  # 废弃
        self.close_push_button = QPushButton('Close')
        self.button_layout = QHBoxLayout()
        self.button_layout.addWidget(self.add_push_button)
        self.button_layout.addWidget(self.remove_push_button)
        self.button_layout.addWidget(self.remove_all_push_button)
        self.button_layout.addWidget(self.save_push_button)
        self.button_layout.addWidget(self.calibration_push_button)
        self.button_layout.addWidget(self.calibration_a_line_push_button)
        self.button_layout.addWidget(self.load_a_line_loss_push_button)
        self.button_layout.addWidget(self.load_target_table_button)
        self.button_layout.addWidget(self.json_to_excell_button)
        self.button_layout.addWidget(self.subtracting_push_button)
        self.button_layout.addWidget(self.close_push_button)

        self.h = QHBoxLayout()
        self.h.addLayout(self.left_layout)
        self.h.addLayout(self.right_layout)
        self.h.setStretch(0, 3)
        self.h.setStretch(1, 7)

        # 布局
        self.v = QVBoxLayout()
        self.v.addLayout(self.h)
        self.v.addLayout(self.button_layout)

        self.setLayout(self.v)

        # 表格
        self.model = QStandardItemModel()
        self.model.setColumnCount(2)
        self.model.setHorizontalHeaderLabels(['Frequency', 'Loss'])
        self.loss_table_view.setModel(self.model)
        horizontal_header = self.loss_table_view.horizontalHeader()
        # 均匀拉直表头
        horizontal_header.setSectionResizeMode(QHeaderView.Stretch)
        # 表头居中
        horizontal_header.setDefaultAlignment(Qt.AlignHCenter)

        # 添加代理
        self.item_delegate = SpinBoxDelegate()
        self.loss_table_view.setItemDelegate(self.item_delegate)


class LossDialog(UiLossDialog):
    request_signal = pyqtSignal(str, int)
    ask_signal = pyqtSignal(str, int, str)
    message_emitted = pyqtSignal(str)
    input_signal = pyqtSignal(str, dict, int)

    def __init__(self, parent=None):
        super(LossDialog, self).__init__(parent)

        # 数据
        self.loss_dict = dict()
        self.copy_loss_dict = copy.deepcopy(self.loss_dict)

        self.current_index = -1

        # 加载下拉框
        self.entrances = ['Port 1', 'Port 2', 'Port 3', 'Port 4', 'Port 5', 'Port 6', 'Port 7', 'Port 8']
        self.entrance_combox.addItems(self.entrances)
        self.instrument_names = [
            'signal analyzer',
            'vector signal generator',
            'vector signal generator 2',
            'analog signal generator',
            'Wideband Radio Communication Tester',
            'Power Sensor'
        ]
        self.instrument_combox.addItems(self.instrument_names)
        self.inside_or_outside_combox.addItems(['Outside', 'Inside'])

        # 衰减器布局
        self.port_att_group = QGroupBox('Front Port Att')
        self.att_layout = QFormLayout()
        self.port_att_group.setLayout(self.att_layout)
        # 前端衰减器
        self.att_spinbox_info = {}
        for port in range(1, 9):
            att_value_spin = QDoubleSpinBox()
            att_check_box = QCheckBox(f"Port {port} Att")
            self.att_spinbox_info[port] = [att_check_box, att_value_spin]
            self.att_layout.addRow(att_check_box, att_value_spin)
            att_value_spin.valueChanged.connect(self.save_front_att_info)
            att_check_box.stateChanged.connect(self.save_front_att_info)
            pass

        self.left_layout.addWidget(self.port_att_group)

        self.port_att_group_for_instrument = QGroupBox('Instrument Port Att')
        self.att_layout_for_instrument = QFormLayout()
        self.port_att_group_for_instrument.setLayout(self.att_layout_for_instrument)
        # 前端衰减器
        self.att_spinbox_info_for_instrument = {}
        for instrument_name in self.instrument_names:
            att_value_spin = QDoubleSpinBox()
            att_check_box = QCheckBox(f"{instrument_name}")
            self.att_spinbox_info_for_instrument[instrument_name] = [att_check_box, att_value_spin]
            self.att_layout_for_instrument.addRow(att_check_box, att_value_spin)
            att_value_spin.valueChanged.connect(self.save_inst_att_info)
            att_check_box.stateChanged.connect(self.save_inst_att_info)
            pass

        self.left_layout.addWidget(self.port_att_group_for_instrument)

        # 文件
        if not os.path.exists(LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\'):
            os.makedirs(LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\')
        self.current_file_name = LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\' \
                                 + self.entrance_combox.currentText() + '-' \
                                 + self.instrument_combox.currentText()

        self.show_loss()
        self.load_front_att_info()
        self.load_inst_att_info()

        # 线程
        self.calibration_thread = CalibrationThreadOfSwitchBox()


        # 信号与槽
        self.calibration_thread.power_progress_signal.connect(self.power_progress_bar.setValue)
        self.calibration_thread.message_signal.connect(self.log_view.appendHtml)
        self.calibration_thread.finished.connect(self.calibration_finish)
        self.add_push_button.clicked.connect(self.add_new_loss)
        self.save_push_button.clicked.connect(self.save_loss_file_1)
        self.calibration_thread.save_loss_info_signal.connect(self.save_loss_file_2)
        self.close_push_button.clicked.connect(self.close)
        self.remove_all_push_button.clicked.connect(self.remove_all)
        self.remove_push_button.clicked.connect(self.remove_row)
        self.entrance_combox.currentIndexChanged.connect(self.show_loss)
        self.instrument_combox.currentIndexChanged.connect(self.show_loss)
        self.inside_or_outside_combox.currentIndexChanged.connect(self.show_loss)
        self.calibration_push_button.clicked.connect(self.calibration)
        self.calibration_a_line_push_button.clicked.connect(self.calibration_a_line)
        self.load_a_line_loss_push_button.clicked.connect(self.load_one_line_loss_info)
        self.load_target_table_button.clicked.connect(self.load_loss_info)
        self.json_to_excell_button.clicked.connect(self.json_to_excell)
        self.subtracting_push_button.clicked.connect(self.subtracting)

        self.model.itemChanged.connect(self.model_change)

        self.is_switch = False

        # 状态
        self.is_pause = False

        # 回复
        self.current_answer = None

        # 数据
        self.instruments = {}
        self.signal_analyzer = None
        self.signal_generator = None
        self.power_sensor = None
        self.switch_box = None
        self.new_item = None

    @staticmethod
    def get_tx_on_and_tx_off_samples(samples, threshold):
        tx_on_samples = []
        tx_off_samples = []
        for sample in samples:
            power = sample[1]
            if power > threshold:
                tx_on_samples.append(sample)
            else:
                tx_off_samples.append(sample)
        return tx_on_samples, tx_off_samples

    @staticmethod
    def divide_into_groups(samples: list, one_point_time):
        """
            给samples按间距进行分类。
        """
        # 容器
        # groups = [group, group, group]
        groups = []
        group = []
        if not samples:
            return groups
        # limit = float('1e-06')
        first_sample = samples[0]
        last_sample = samples[-1]
        for index, sample in enumerate(samples):
            if sample == first_sample:
                group.append(first_sample)
                continue
            if sample == last_sample:
                group.append(last_sample)
                break
            else:
                # 获取下一个点——第三个点
                next_sample = samples[index + 1]

                # 第二个点和第三个点的时间
                sample_time = sample[0]
                next_sample_time = next_sample[0]
                interval = next_sample_time - sample_time

                if interval > one_point_time * 2:
                    group.append(sample)
                    groups.append(group)
                    group = []
                    continue
                group.append(sample)
        if group:
            groups.append(group)
            group = []
        return groups

    def save_front_att_info(self):
        front_port_att_info = {}
        for port in range(1, 9):
            check_box, spin_box = self.att_spinbox_info[port]
            front_port_att_info[port] = [check_box.isChecked(), spin_box.value()]
        # 保存路径
        save_path = os.path.join(LOSS_PATH, "ATT INFO")
        if not os.path.exists(save_path):
            os.makedirs(save_path)

        save_json(front_port_att_info, os.path.join(save_path, "front_port_att_info.json"))

    def save_inst_att_info(self):
        inst_port_att_info = {}
        for instrument_name in self.instrument_names:
            check_box, spin_box = self.att_spinbox_info_for_instrument[instrument_name]
            inst_port_att_info[instrument_name] = [check_box.isChecked(), spin_box.value()]

        # 保存路径
        save_path = os.path.join(LOSS_PATH, "ATT INFO")
        if not os.path.exists(save_path):
            os.makedirs(save_path)

        save_json(inst_port_att_info, os.path.join(save_path, "inst_port_att_info.json"))

    def load_front_att_info(self):
        # 加载前端端口衰减器信息
        file_path = os.path.join(LOSS_PATH, "ATT INFO", "front_port_att_info.json")
        if os.path.exists(file_path):
            with open(file_path, 'r') as f:
                front_port_att_info = json.load(f)
                for port, value in front_port_att_info.items():
                    port_num = int(port)
                    if port_num in self.att_spinbox_info:
                        check_box, spin_box = self.att_spinbox_info[port_num]

                        try:
                            check_box.setChecked(value[0])
                            spin_box.setValue(float(value[1]))
                        except:
                            pass
        else:
            # QMessageBox.warning(self, 'Load', 'Front port att info file not found!')
            pass

    def load_inst_att_info(self):
        # 加载仪器衰减器信息
        file_path = os.path.join(LOSS_PATH, "ATT INFO", "inst_port_att_info.json")
        if os.path.exists(file_path):
            with open(file_path, 'r') as f:
                inst_port_att_info = json.load(f)
                for instrument_name, value in inst_port_att_info.items():
                    if instrument_name in self.att_spinbox_info_for_instrument:
                        check_box, spin_box = self.att_spinbox_info_for_instrument[instrument_name]
                        try:
                            check_box.setChecked(value[0])
                            spin_box.setValue(float(value[1]))
                        except:
                            pass
        else:
            # QMessageBox.warning(self, 'Load', 'Instrument att info file not found!')
            pass


    def working_1(self):
        self.hide()
        try:
            # self.calibration_progess_dialog.open()
            self.calibration_thread.task = self.calibration
            self.calibration_thread.start()
        except Exception as e:
            # QMessageBox.about(self, 'RF line', str(e))
            self.show()
            pass

    def working_2(self):
        # self.calibration_progess_dialog.open()
        self.calibration_thread.task = self.calibration_a_line
        self.calibration_thread.start()

    def waiting_for_confirmation(self, request: str, timeout: int = 0):
        """
            让主窗口弹出对话框，并且等待用户/测试员点击确认。
        """
        print(request)
        self.request_signal.emit(request, timeout)
        self.is_pause = True
        while self.is_pause:
            time.sleep(0.1)
        print('OK!')

    def waiting_for_input(self, input_dialog_type: str, info: dict, timeout: int = 0):
        self.input_signal.emit(input_dialog_type, info, timeout)
        self.is_pause = True
        while self.is_pause:
            time.sleep(0.1)

    def waiting_for_ask(self, ask: str, timeout: int = 0, default_action='Yes'):
        """
            让主窗口弹出对话框，并且等待用户/测试员点击Yes or No。
        """
        print(ask)
        self.ask_signal.emit(ask, timeout, default_action)
        self.is_pause = True
        while self.is_pause:
            time.sleep(0.1)

    def show_finished_message(self):
        timeout = 5
        info_box = QMessageBox(self)
        info_box.setIcon(QMessageBox.Information)
        info_box.setText('finished!')
        info_box.setWindowTitle('Information')
        info_box.setStandardButtons(QMessageBox.Ok)
        if timeout:
            # 自动关闭
            info_box.button(QMessageBox.Ok).animateClick(timeout * 1000)
        info_box.exec_()

    def subtracting(self):
        d = DifferenceMethodMainWindow()
        d.exec_()

    def model_changed(self):
        return self.copy_loss_dict != self.loss_dict

    def model_change(self, item: QStandardItem):
        if self.is_switch:
            return
        if self.new_item:
            if self.new_item == item:
                self.new_item = None
                return

        # 在字典中找到对应的键值
        row = item.row()
        column = item.column()
        keys = self.loss_dict.keys()
        keys = sorted([float(key) for key in keys])
        key = keys[row]
        old_value = self.loss_dict[str(key)][0]
        old_value_in_loss = self.loss_dict[str(key)][1]
        old_key = str(key)

        if column == 0:
            if str(old_key) == item.text():
                return
        elif column == 1:
            if str(old_value) == item.text():
                return
        else:
            if str(old_value_in_loss) == item.text():
                return

        # 判断是否为浮点数或整数
        try:
            float(item.text())
        except Exception as e:
            print(e)
            QMessageBox.warning(self, 'error', 'Please enter a number: integer or decimal.')
            # 显示原来的数值
            if column == 0:
                item.setText(str(old_key))
            else:
                item.setText(str(old_value))
                return

        # 根据column判断修改的是frequency还是loss
        if column == 0:
            new_key = str(float(item.text()))
            if new_key not in self.loss_dict:
                self.loss_dict[new_key] = old_value
                del self.loss_dict[old_key]
                self.draw()
            else:
                QMessageBox.warning(self, 'error', 'The key already exists.')
                # 显示原来的数值
                item.setText(str(old_key))
        elif column == 1:
            self.loss_dict[str(key)][0] = float(item.text())
            self.draw()
        else:
            self.loss_dict[str(key)][1] = float(item.text())
            self.draw()

    def show_loss(self):
        if self.inside_or_outside_combox.currentText() == 'Outside':
            self.group_box_of_instrument.hide()
            self.group_box_of_frequency_range.hide()
            self.specify_frequency_check_box.hide()
            self.only_specify_frequency_check_box.hide()
            self.tab_view.setTabVisible(0, False)
            if self.model.columnCount() > 2:
                self.loss_table_view.hideColumn(2)
                self.model.setHorizontalHeaderLabels(['Frequency', 'Loss'])
            if self.instrument_combox.currentText() == 'Wideband Radio Communication Tester':
                self.model.setColumnCount(3)
                self.model.setHorizontalHeaderLabels(['Frequency', 'Out Loss', 'In Loss'])
                self.loss_table_view.showColumn(2)
            self.port_att_group_for_instrument.show()
            self.port_att_group.show()
        else:
            if self.model.columnCount() > 2:
                self.loss_table_view.hideColumn(2)
                self.model.setHorizontalHeaderLabels(['Frequency', 'Loss'])
            self.port_att_group_for_instrument.hide()
            self.port_att_group.hide()
            self.group_box_of_instrument.show()
            self.group_box_of_frequency_range.show()
            self.specify_frequency_check_box.show()
            self.only_specify_frequency_check_box.show()
            self.tab_view.setTabVisible(0, True)
        if self.instrument_combox.currentText() == 'Power Sensor' and self.inside_or_outside_combox.currentText() != 'Outside':
            return
        try:
            self.is_switch = True

            # 当用户选择外部线损时，隐藏【校准】按钮，该按钮只支持内部校准。
            if self.inside_or_outside_combox.currentText() == 'Outside':
                self.calibration_push_button.hide()
                self.calibration_a_line_push_button.hide()
                self.load_target_table_button.hide()
            else:
                self.calibration_push_button.show()
                self.calibration_a_line_push_button.show()
                self.load_target_table_button.show()

            # 询问是否需要保存当前更改?
            self.query()

            # 刷新(清空)
            self.clean_all()

            # 加载对应的线损文件
            self.load_loss_file()

            # 填充线损表格
            self.fill_in_loss_table_view()

            # 绘制线损图形
            self.draw()

            self.is_switch = False
        except Exception as e:
            pass

    def clean_all(self):
        self.loss_dict.clear()
        self.copy_loss_dict.clear()
        self.model.removeRows(0, self.model.rowCount())
        self.line_chart.removeAllSeries()
        self.current_index = -1

    def query(self):
        if self.model_changed():
            answer = QMessageBox.question(self, 'data changed', 'The Loss Data is changed, Do you want to save data?')
            if answer == QMessageBox.Yes:
                self.save_loss_file_1()

    def close(self):
        self.query()
        self.calibration_thread.stop_working = True
        QDialog.close(self)

    def load_loss_file(self):
        if not os.path.exists(LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\'):
            os.makedirs(LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\')
        self.current_file_name = file_name = LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\' + \
                                             self.entrance_combox.currentText() + '-' + \
                                             self.instrument_combox.currentText()
        # 尝试打开（json文件），如果打开失败，则自动创建一个json文件
        if os.path.exists(file_name + '.json'):
            with open(file_name + '.json', 'r', encoding='utf8') as f:
                try:
                    self.loss_dict = json.load(f)
                except Exception as e:
                    print(e)
                    self.create_new_json_file(file_name)
        else:
            self.create_new_json_file(file_name)

        self.copy_loss_dict = copy.deepcopy(self.loss_dict)
        return copy.deepcopy(self.loss_dict)

    def load_loss_file_2(self):
        if not os.path.exists(LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\'):
            os.makedirs(LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\')
        self.current_file_name = file_name = LOSS_PATH + self.inside_or_outside_combox.currentText() + '\\' + \
                                             self.entrance_combox.currentText() + '-' + \
                                             self.instrument_combox.currentText()
        # 尝试打开（json文件），如果打开失败，则自动创建一个json文件
        if os.path.exists(file_name + '.json'):
            with open(file_name + '.json', 'r', encoding='utf8') as f:
                try:
                    loss_dict = json.load(f)
                    return loss_dict
                except Exception as e:
                    self.create_new_json_file(file_name)
                    return {}
        else:
            self.create_new_json_file(file_name)
            return {}

    @staticmethod
    def create_new_json_file(file_name):
        with open(file_name + '.json', 'w', encoding='utf8') as f:
            json.dump(dict(), f, sort_keys=False, indent=4)

    def save_loss_file_1(self, path=None, info = None):
        try:
            if info is None:
                info = self.loss_dict
            if not path:
                path = self.current_file_name + '.json'
            with open(path, 'w', encoding='utf8') as f:
                json.dump(info, f, sort_keys=False, indent=4)
        except Exception as e:
            print(str(e))
            pass

        self.copy_loss_dict = copy.deepcopy(self.loss_dict)

    def save_loss_file_2(self, info=None):
        """
            校准线程完成后执行
        """
        if not info:
            info = self.calibration_thread.loss_info
        if not info:
            QMessageBox.warning(self, 'Save', 'The data to be saved is empty.')
            return
        try:
            # 弹窗，询问要保存的到哪里。
            if self.calibration_thread.object_of_calibration == 'Switch Box':
                name = self.entrance_combox.currentText() + '-' \
                       + self.instrument_combox.currentText()
            else:
                name = 'line loss'
            filename, file_format = QFileDialog.getSaveFileName(
                self,
                f'Save',
                f'C:\\Temp\\Loss\\{name}.json',
                'json(*.json)'
            )
            with open(filename, 'w', encoding='utf8') as f:
                json.dump(info, f, sort_keys=False, indent=4)
            QMessageBox.about(self, 'Success', 'Success!')
        except Exception as e:
            QMessageBox.about(self, 'Fail', 'Save Data Fail!')

    def fill_in_loss_table_view(self):
        try:
            keys = sorted([float(key) for key in self.loss_dict.keys()])
            for row_index, key in enumerate(keys):
                self.current_index = row_index
                if self.model.columnCount() > 2:
                    frequency, loss, in_loss = key, self.loss_dict[str(key)][0], self.loss_dict[str(key)][1]
                else:
                    frequency, loss = key, self.loss_dict[str(key)]
                    if type(loss) is list:
                        in_loss = loss[1]
                        loss = loss[0]
                        self.loss_dict[str(key)] = loss
                # frequency = float(frequency.split(':')[1])
                frequency_cell = QStandardItem(str(frequency))
                frequency_cell.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
                self.model.setItem(row_index, 0, frequency_cell)

                loss_cell = QStandardItem(str(round(loss, 2)))
                loss_cell.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
                self.model.setItem(row_index, 1, loss_cell)

                if self.model.columnCount() > 2:
                    in_loss_cell = QStandardItem(str(round(in_loss, 2)))
                    in_loss_cell.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
                    self.model.setItem(row_index, 2, in_loss_cell)
        except Exception as e:
            pass

    def draw(self):
        if not self.loss_dict:
            return

        # 清除之前绘制的图形
        self.line_chart.removeAllSeries()
        # 遍历samples，往series填充每个点——(x, y)
        series = QLineSeries()
        self.line_chart.addSeries(series)
        # 开启OpenGL,提高绘图效率
        series.setUseOpenGL(True)
        keys = sorted([float(key) for key in self.loss_dict.keys()])
        for key in keys:
            x = key
            key = str(key)
            try:
                y = self.loss_dict[key][0]
            except Exception as e:
                y = self.loss_dict[key]
                self.loss_dict[key] = [y, y]
            series.append(float(x), float(y))

        # 其他
        self.line_chart.createDefaultAxes()  # 创建默认的轴

        # 确定y轴范围
        power_list = [abs(float(self.loss_dict[x][0])) for x in self.loss_dict.keys()]
        y_min = min(power_list) - 1
        y_max = max(power_list) + 1
        self.line_chart.axisY().setRange(0, 30)  # 设置y1轴范围
        self.line_chart.axisY().setTickCount(11)  # y1轴设置10个刻度

        # 确定x轴的范围
        frequency_list = [float(x) for x in self.loss_dict.keys()]
        x_min = min(frequency_list)
        x_max = max(frequency_list)
        self.line_chart.axisX().setRange(x_min, x_max)

    def add_new_loss(self):
        dialog = NewLossDialog(self)
        dialog.exec_()
        if dialog.frequency:
            if dialog.frequency not in self.loss_dict:
                self.current_index += 1
                dialog.frequency = str(dialog.frequency)
                self.loss_dict[dialog.frequency] = [0, 0]
                self.loss_dict[dialog.frequency][0] = dialog.loss
                self.loss_dict[dialog.frequency][1] = dialog.loss
                self.add_new_row(dialog.frequency, str(dialog.loss))
                self.draw()
            else:
                QMessageBox.about(self, 'error', 'The key already exists.')

    def add_new_row(self, frequency: str, loss: str):
        # self.model.blockSignals(True)
        self.new_item = frequency_cell = QStandardItem(frequency)
        frequency_cell.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.model.setItem(self.current_index, 0, frequency_cell)

        self.new_item = loss_cell = QStandardItem(loss)
        loss_cell.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.model.setItem(self.current_index, 1, loss_cell)

        if self.model.columnCount() > 2:
            self.new_item = loss_cell = QStandardItem(loss)
            loss_cell.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            self.model.setItem(self.current_index, 2, loss_cell)
        # self.model.blockSignals(False)

    def remove_all(self):
        self.loss_dict.clear()
        self.model.removeRows(0, self.model.rowCount())
        self.line_chart.removeAllSeries()
        self.current_index = -1
        # 清除之前绘制的图形
        self.line_chart.removeAllSeries()

    def remove_row(self):
        index = self.loss_table_view.currentIndex()
        row = index.row()
        if row == -1:
            return
        # 处理字典
        keys = self.loss_dict.keys()
        keys = sorted([float(key) for key in keys])
        key = keys[row]
        del self.loss_dict[str(key)]

        # 处理模型
        print(row)
        self.model.removeRow(row)

        # 绘制线损图形
        self.draw()

    def calibration_of_signal_analyzer(self):
        """
            校准频谱仪那一路。
            频谱仪这一路中，法规300328的【Transmitter unwanted emissions in the out-of-band domain】
            步进需要缩紧至0.5 MHz
            两段范围：[2399.5, 2399.5 - 2 * 20]， [2484, 2484 + 2 * 20]
        """
        loss_info = {}
        frequency_ranges = [
            # start, stop, step, rbw
            [30, 1000, 10, 0.01],  # 杂散测试需要
            [1000, 12700, 100, 1],  # 杂散测试需要
            # [12700, 13000, 100, 1],  # WiFi 6E 杂散测试需要到26GHz
            [12700, 26000, 100, 1],  # WiFi 6E 杂散测试需要到26GHz
            [2399.5 - 2 * 20, 2399.5, 0.5, 1],  # EN 300 328 OOB
            [2484, 2484 + 2 * 20, 0.5, 1],  # EN 300 328 OOB
            [5945, 6425 + 5, 5, 1],  # EN 303 687  6GHz 频段
        ]
        # 设置频谱仪
        self.set_up_signal_analyzer_for_calibration()
        # 确定level和frequency
        output_power = 0
        self.signal_generator.set_amplitude(output_power)
        self.signal_analyzer.set_display_line(output_power)
        self.signal_generator.set_frequency("30 MHz")
        self.signal_generator.set_rf(1)
        self.signal_generator.set_modulation(0)
        self.signal_analyzer.xapp_connectivity_set_sa_rflevel(output_power + 20)
        for start, stop, step, rbw in frequency_ranges:
            self.signal_analyzer.xapp_connectivity_set_sa_res_bandwidth(f"{rbw} MHz")
            while start <= stop:
                max_power = self.get_real_power(start)
                loss = round(output_power - max_power, 2)
                key = round(float(start), 2)
                loss_info[key] = loss
                start += step
        self.signal_generator.set_rf(0)
        return loss_info

    def get_real_power(self, frequency):
        self.signal_analyzer.xapp_nr_set_continue_state('OFF')
        self.signal_analyzer.xapp_connectivity_set_center_frequency(f"{frequency} MHz")
        self.signal_generator.set_frequency(f"{frequency} MHz")
        time.sleep(0.5)
        self.signal_analyzer.sweep()
        time.sleep(0.5)
        self.signal_analyzer.xapp_common_query_oper_stat()
        self.signal_analyzer.xapp_connectivity_get_sa_peak(1)
        max_power = self.signal_analyzer.get_mark_power(1)
        return max_power

    def calibration_of_tester_box(self):
        """
            校准综测仪那一路。
        """
        loss_info = {}
        frequency_ranges = [
            [2402, 2480 + 1, 1, 1],  # EN 300 328 2.4GHz 频段
            [5150, 5750 + 5, 5, 1],  # EN 301 893 5GHz 频段
            [5750 + 5, 5825, 5, 1],  # EN 300 440 5.8GHz 频段
            [5945, 6425 + 20, 20, 1],  # EN 303 687 6GHz 频段
        ]
        self.signal_analyzer.xapp_connectivity_set_sa_freq_span('500 kHz')
        self.signal_analyzer.xapp_connectivity_set_center_frequency("30 MHz")
        self.signal_analyzer.set_detector('POSITIVE')
        output_power = 0
        self.signal_generator.set_amplitude(output_power)
        self.signal_analyzer.set_display_line(output_power)
        self.signal_generator.set_rf(1)
        self.signal_generator.set_modulation(0)
        for start, stop, step, rbw in frequency_ranges:
            self.signal_analyzer.xapp_connectivity_set_sa_res_bandwidth(f"{rbw} MHz")
            while start <= stop:
                max_power = self.get_real_power(start)
                loss = round(output_power - max_power, 2)
                key = round(float(start), 2)
                loss_info[key] = loss
                start += step
        self.signal_generator.set_rf(0)
        return loss_info

    def calibration_of_analog_generator(self):
        """
            校准模拟信号源那一路。
        """
        loss_info = {}
        frequency_ranges = [
            # [干扰频率], Power
            [[2395, 2488.5], -35],  # EN 300 328 2.4GHz 频段 自适应（CW）
            [[2380, 2504, 2300, 2330, 2360, 2524, 2584, 2674], -34],  # EN 300 328 2.4GHz 频段 Blocking
            [[5100], -53],  # EN 301 893 5GHz 频段 Blocking
            [[4900, 5000, 5975], -47],  # EN 301 893 5GHz 频段 Blocking
            [[5895, 6475], -53],  # EN 303 687 6GHz 频段 Blocking
            [[5695, 5795, 6575, 6675], -47],  # EN 303 687 6GHz 频段 Blocking
        ]
        self.signal_analyzer.xapp_connectivity_set_sa_freq_span('500 kHz')
        self.signal_analyzer.set_detector('POSITIVE')
        self.signal_analyzer.xapp_connectivity_set_sa_res_bandwidth(f"1 MHz")
        output_power = 0
        self.signal_generator.set_amplitude(output_power)
        self.signal_analyzer.set_display_line(output_power)
        self.signal_generator.set_rf(1)
        self.signal_generator.set_modulation(0)
        for cw_frequencies, target_power in frequency_ranges:
            target_power = 0
            self.signal_generator.set_amplitude(target_power)
            self.signal_analyzer.set_display_line(target_power)
            for cw_frequency in cw_frequencies:
                max_power = self.get_real_power(cw_frequency)
                loss = round(output_power - max_power, 2)
                key = round(float(cw_frequency), 2)
                loss_info[key] = loss
        self.signal_generator.set_rf(0)
        return loss_info

    def calibration_of_power_sensor(self):
        """
            校准矢量功率计那一路。
            power_sensor: 采集卡 + RMS功率传感器
        """
        pass

    def set_up_signal_analyzer_for_calibration(self):
        """
            设置频谱仪，准备校准
        """
        self.signal_analyzer.xapp_nr_set_continue_state('OFF')
        self.signal_analyzer.xapp_connectivity_set_sa_freq_span('500 kHz')
        self.signal_analyzer.xapp_connectivity_set_center_frequency("30 MHz")
        self.signal_analyzer.set_detector('POSITIVE')
        pass

    def set_up_signal_analyzer_for_calibration_2(self):
        """
            设置频谱仪，准备校准
        """
        self.signal_analyzer.xapp_nr_set_continue_state('OFF')
        self.signal_analyzer.xapp_connectivity_set_sa_freq_span('500 kHz')
        self.signal_analyzer.xapp_connectivity_set_center_frequency("30 MHz")
        self.signal_analyzer.set_detector('POSITIVE')
        pass

    def calibration(self):
        # 确保加载了一根线线损
        if not self.calibration_thread.line_loss_info:
            QMessageBox.warning(self, 'Error', 'Please perform line loss calibration first.')
            return
        self.calibration_thread.object_of_calibration = 'Switch Box'
        # 禁用控件——避免用户更改
        self.set_entable_widgets(False)
        try:
            self.loss_info = {}
            # --- Step 1： 确定用户选择的路线 ---
            start = self.entrance_combox.currentText()
            stop = self.instrument_combox.currentText()
            if 'Port' in start:
                port = int(start[-1])
            else:
                port = 1
            instrument_name = stop

            # --- Step 2：连接相关仪表 ---
            try:
                if self.sa_info_widget.instrument is not None:
                    self.sa_info_widget.scpi_emitted.disconnect(self.log_view.appendHtml)
                if self.sg_info_widget.instrument is not None:
                    self.sg_info_widget.scpi_emitted.disconnect(self.log_view.appendHtml)
            except Exception as e:
                print(str(e))
                pass

            try:
                if self.sa_info_widget.instrument is not None:
                    self.sa_info_widget.scpi_emitted.connect(self.log_view.appendHtml)
                if self.sg_info_widget.instrument is not None:
                    self.sg_info_widget.scpi_emitted.connect(self.log_view.appendHtml)
                # self.daq.scpi_emitted.connect(self.log_view.appendHtml)
            except Exception as e:
                print(str(e))
                pass
            # 获取仪表
            self.calibration_thread.sa = self.sa_info_widget.instrument
            self.calibration_thread.vsg = self.sg_info_widget.instrument
            switch_box = self.calibration_thread.switch_box = self.switch_box_widget.instrument

            # --- Step 3：确定校准任务 ---
            start_frequency = self.start_frequency_spin.value()
            stop_frequency = self.stop_frequency_spin.value()
            step = self.step_frequency_spin.value()
            frequency = start_frequency
            frequencies = []
            while frequency <= stop_frequency:
                frequencies.append(frequency)
                frequency += step

            # 再加上特定的频率
            special_frequency_points = []
            special_frequency_points += [2395, 2488.5]
            special_frequency_points += [2380, 2504, 2300, 2330, 2360, 2524, 2584, 2674]
            special_frequency_points += [5100]
            special_frequency_points += [4900, 5000, 5975]
            special_frequency_points += [5895, 6475]
            special_frequency_points += [5695, 5795, 6575, 6675]
            special_frequency_points += [2395, 2488.5]
            special_frequency_points += wifi_5g_frequencies
            special_frequency_points += wifi_6g_frequencies
            special_frequency_points += frequencies_of_class_bluetooth
            special_frequency_points += [2380, 2504, 2300, 2330, 2360, 2524, 2584, 2674]

            if self.only_specify_frequency_check_box.isChecked():
                frequencies = special_frequency_points
            else:
                if self.specify_frequency_check_box.isChecked():
                    frequencies += special_frequency_points
            frequencies = list(set(frequencies))
            frequencies = sorted(frequencies)
            self.message_emitted.emit(f"Frequencies lenght: {len(frequencies)}")
            rbw_1 = self.rbw_editer_1.text()
            vbw_1 = self.vbw_editer_1.text()
            rbw_2 = self.rbw_editer_2.text()
            vbw_2 = self.vbw_editer_2.text()
            span_1 = self.span_editer_1.text()
            span_2 = self.span_editer_2.text()
            tasks = []
            frequency = start_frequency
            for frequency in frequencies:
                if frequency < 1000:
                    rbw = rbw_1
                    vbw = vbw_1
                    span = span_1
                else:
                    rbw = rbw_2
                    vbw = vbw_2
                    span = span_2
                task = {
                    'center frequency': frequency,
                    'span': span,
                    'rbw': rbw,
                    'vbw': vbw
                }
                tasks.append(task)
            self.calibration_thread.tasks = tasks
            # --- Step 4: 切换开关箱 ---
            # 切换内部线路
            switch_box.port = port
            switch_box.to_default_test_state()

            # --- Step 5：保存 ---
            # 开启线程, 开始校准
            self.calibration_thread.start()

        except Exception as e:
            # QMessageBox.warning(self, 'error', str(e))
            self.message_emitted.emit(str(e))
            print(str(e))

    def calibration_a_line(self):
        # 确保加载了一根线线损
        self.calibration_thread.object_of_calibration = 'Line Loss'
        # 禁用控件——避免用户更改
        self.set_entable_widgets(False)
        try:
            # --- Step 1： 确定用户选择的路线 ---


            # --- Step 2：连接相关仪表 ---
            try:
                if self.sa_info_widget.instrument is not None:
                    self.sa_info_widget.scpi_emitted.connect(self.log_view.appendHtml)
                if self.sg_info_widget.instrument is not None:
                    self.sg_info_widget.scpi_emitted.connect(self.log_view.appendHtml)
                # self.daq.scpi_emitted.connect(self.log_view.appendHtml)
            except Exception as e:
                print(str(e))
                pass
            # 获取仪表
            self.calibration_thread.sa = self.sa_info_widget.instrument
            self.calibration_thread.vsg = self.sg_info_widget.instrument
            # self.calibration_thread.switch_box = self.switch_box_widget.instrument

            # --- Step 3：确定校准任务 ---
            start_frequency = self.start_frequency_spin.value()
            stop_frequency = self.stop_frequency_spin.value()
            step = self.step_frequency_spin.value()
            frequency = start_frequency
            frequencies = []
            while frequency <= stop_frequency:
                frequencies.append(frequency)
                frequency += step

            # 再加上特定的频率
            special_frequency_points = []
            special_frequency_points += [2395, 2488.5]
            special_frequency_points += [2380, 2504, 2300, 2330, 2360, 2524, 2584, 2674]
            special_frequency_points += [5100]
            special_frequency_points += [4900, 5000, 5975]
            special_frequency_points += [5895, 6475]
            special_frequency_points += [5695, 5795, 6575, 6675]
            special_frequency_points += [2395, 2488.5]
            special_frequency_points += wifi_5g_frequencies
            special_frequency_points += wifi_6g_frequencies
            special_frequency_points += frequencies_of_class_bluetooth
            special_frequency_points += [2380, 2504, 2300, 2330, 2360, 2524, 2584, 2674]

            if self.only_specify_frequency_check_box.isChecked():
                frequencies = special_frequency_points
            else:
                if self.specify_frequency_check_box.isChecked():
                    frequencies += special_frequency_points
            frequencies = list(set(frequencies))
            frequencies = sorted(frequencies)

            rbw_1 = self.rbw_editer_1.text()
            vbw_1 = self.vbw_editer_1.text()
            rbw_2 = self.rbw_editer_2.text()
            vbw_2 = self.vbw_editer_2.text()
            span_1 = self.span_editer_1.text()
            span_2 = self.span_editer_2.text()
            tasks = []
            frequency = start_frequency
            for frequency in frequencies:
                if frequency < 1000:
                    rbw = rbw_1
                    vbw = vbw_1
                    span = span_1
                else:
                    rbw = rbw_2
                    vbw = vbw_2
                    span = span_2
                task = {
                    'center frequency': frequency,
                    'span': span,
                    'rbw': rbw,
                    'vbw': vbw
                }
                tasks.append(task)
            self.calibration_thread.tasks = tasks
            # --- Step 4: 切换开关箱 ---
            # 切换内部线路
            # self.switch_box.port = port
            # self.switch_box.to_default_test_state()

            # --- Step 5：保存 ---
            # 开启线程, 开始校准
            self.calibration_thread.start()
        except Exception as e:
            # QMessageBox.warning(self, 'error', str(e))
            self.message_emitted.emit(str(e))
            print(str(e))

    def load_one_line_loss_info(self):
        # 弹窗，询问要保存的到哪里。
        try:
            filename, file_format = QFileDialog.getOpenFileName(
                self,
                f'Load',
                'C:\\Temp\\Loss',
                'json(*.json)'
            )
            with open(filename, 'r', encoding='utf8') as f:
                info = json.load(f)
                if self.inside_or_outside_combox.currentText() != 'Outside':
                    self.calibration_thread.line_loss_info = info
                else:
                    self.loss_dict = info
                    self.copy_loss_dict = copy.deepcopy(info)
                    self.save_loss_file_1()
                    self.show_loss()
            QMessageBox.about(self, 'Success', 'Loading succeeded!')
        except Exception as e:
            print(str(e))
            pass

    def load_loss_info(self):
        """
            加载之前校准的数据
        """
        # 弹窗，询问要保存的到哪里。
        try:
            filename, file_format = QFileDialog.getOpenFileName(
                self,
                f'Load',
                'C:\\Temp\\Loss',
                'json(*.json)'
            )
            with open(filename, 'r', encoding='utf8') as f:
                self.calibration_thread.loss_info = json.load(f)
                self.calibration_thread.is_load_data = True
            QMessageBox.about(self, 'Success', 'Loading succeeded!')
        except Exception as e:
            print(str(e))
            pass

    def json_to_excell(self):
        # 弹窗，询问要保存的到哪里。
        try:
            filename, file_format = QFileDialog.getOpenFileName(
                self,
                f'Load',
                'C:\\Temp\\Loss',
                'json(*.json)'
            )
            with open(filename, 'r', encoding='utf8') as f:
                loss_info = json.load(f)
                # 新建Excell文档
                wb = openpyxl.Workbook()
                # 获取活跃的工作表，ws代表wb(工作簿)的一个工作表
                ws = wb.active
                rows = []
                for key, value in loss_info.items():
                    rows.append([float(key), float(value)])
                # 冒泡排序
                rows = sorted(rows, key=(lambda row: float(row[0])))
                for row in rows:
                    ws.append(row)
                try:
                    # 弹窗，询问要保存的到哪里。
                    filename, file_format = QFileDialog.getSaveFileName(
                        self,
                        f'Save',
                        f'C:\\Temp\\Loss\\{os.path.splitext(filename)[0]}.xlsx',
                        'xlsx(*.xlsx)'
                    )
                    wb.save(filename)
                    QMessageBox.about(self, 'Success', 'Success!')
                except Exception as e:
                    QMessageBox.about(self, 'Fail', f'Save Data Fail!\n{str(e)}')
        except Exception as e:
            pass

    def set_entable_widgets(self, is_entable: bool):
        # GroupBox
        self.group_box_of_path.setEnabled(is_entable)
        self.group_box_of_instrument.setEnabled(is_entable)
        self.group_box_of_frequency_range.setEnabled(is_entable)

        # 按钮
        self.add_push_button.setEnabled(is_entable)
        self.remove_push_button.setEnabled(is_entable)
        self.remove_all_push_button.setEnabled(is_entable)
        self.save_push_button.setEnabled(is_entable)
        self.calibration_push_button.setEnabled(is_entable)
        self.calibration_a_line_push_button.setEnabled(is_entable)
        self.subtracting_push_button.setEnabled(is_entable)

        # check box
        self.specify_frequency_check_box.setEnabled(is_entable)
        self.only_specify_frequency_check_box.setEnabled(is_entable)

    def calibration_finish(self):
        QMessageBox.about(self, 'Calibration', 'Calibration completed!')
        self.set_entable_widgets(True)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = LossDialog()
    win.show()
    sys.exit(app.exec_())
